package api

import (
	"encoding/json"
	"proxy"
)

// 由于历史原因，Request.Sdp是string，而Response.Sdp是object，
// todo: 等Proxy稳定后，将其统一为object
type Request struct {
	Op              string              `json:"op"`
	TsxId           string              `json:"tsxId"`
	Sdp             *SessionDescription `json:"sdp,omitempty"`
	CbUrl           string              `json:"cburl,omitempty"`
	CtxId           string              `json:"ctxId,omitempty"`
	ConfType        string              `json:"confType,omitempty"`
	Ctx             string              `json:"ctx,omitempty"`
	VCodecs         []string            `json:"vcodecs,omitempty"`
	CSrc            int                 `json:"csrc,omitempty"`
	RtcId           string              `json:"rtcId,omitempty"`
	Candidates      []Candidate         `json:"candidates,omitempty"`
	SubRtcId        string              `json:"subRtcId,omitempty"`
	PubRtcId        string              `json:"pubRtcId,omitempty"`
	Audio           bool                `json:"audio,omitempty"`
	Video           bool                `json:"video,omitempty"`
	LinkAddr        string              `json:"link-addr,omitempty"`
	RequireCheckRR  *int                `json:"requireCheckRR,omitempty"`
	RecPriority     int                 `json:"priority,omitempty"`
	RecType         string              `json:"type,omitempty"`
	RecTimeout      int                 `json:"timeout_ms,omitempty"`
	RecSubDir       string              `json:"sub_dir,omitempty"`
	RecScene        string              `json:"scene,omitempty"`
	RecSource       string              `json:"source,omitempty"`
	RecSegment      string              `json:"segment,omitempty"`
	DropAudio       *bool               `json:"drop_audio,omitempty"`
	DropVideo       *bool               `json:"drop_video,omitempty"`
	PeerAudioSSRCs  []uint32            `json:"peerAudioSSRCs,omitempty"`
	PeerVideoSSRCs  []uint32            `json:"peerVideoSSRCs,omitempty"`
	MaxVideoBitrate *int                `json:"maxVideoBitrate,omitempty"` // kbps
	MinVideoBitrate *int                `json:"minVideoBitrate,omitempty"` // kbps
	AcType          int                 `json:"acType,omitempty"`          // 动作类型。1：开始录制；2：停止录制。
}

func (r *Request) GetMaxVideoBitrate() interface{} {
	if r.MaxVideoBitrate == nil {
		return nil
	}
	return *r.MaxVideoBitrate
}

func (r *Request) GetMinVideoBitrate() interface{} {
	if r.MinVideoBitrate == nil {
		return nil
	}
	return *r.MinVideoBitrate
}

func (r *Request) Encode() []byte {
	//buf, err := json.MarshalIndent(r, "", "\t")
	buf, err := json.Marshal(r)
	if err != nil {
		log.Panicf("could not encode request: %s", r)
	}
	return buf
}

type Response struct {
	Op         string              `json:"op"`
	Status     int                 `json:"status"`
	Message    string              `json:"message"`
	TsxId      string              `json:"tsxId"`
	RtcId      string              `json:"rtcId,omitempty"`
	Sdp        *SessionDescription `json:"sdp,omitempty"`
	Candidates []Candidate         `json:"candidates,omitempty"`
	LinkAudio  bool                `json:"link-audio,omitempty"`
	LinkVideo  bool                `json:"link-video,omitempty"`
	LinkUpdate bool                `json:"link-update,omitempty"`
	LinkAddr   string              `json:"link-addr,omitempty"`
	MixerAddrs []string            `json:"mixer-addrs,omitempty"`
	Api        string              `json:"api,omitempty"`
	RecScene   string              `json:"scene,omitempty"`
	RecSource  string              `json:"source,omitempty"`
	RecSegment string              `json:"segment,omitempty"`
	RecTlv     string              `json:"tlv,omitempty"`
	Meta       *MetaStatus         `json:"meta,omitempty"`
	// ssrcs of local stream, used in subR
	AudioSSRCs []uint32 `json:"audioSSRCs,omitempty"`
	VideoSSRCs []uint32 `json:"videoSSRCs,omitempty"`
	// ssrcs of relay stream, used in initC and ansC
	RelayAudioSSRCs []uint32 `json:"relayAudioSSRCs,omitempty"`
	RelayVideoSSRCs []uint32 `json:"relayVideoSSRCs,omitempty"`
	DropAudio       *bool    `json:"drop_audio,omitempty"`
	DropVideo       *bool    `json:"drop_video,omitempty"`
}

func (r *Response) Encode() []byte {
	//buf, err := json.MarshalIndent(r, "", "\t")
	buf, err := json.Marshal(r)
	if err != nil {
		log.Panicf("could not encode response: %s", err)
	}
	return buf
}

type Candidate struct {
	Mid           string `json:"mid"`
	MlineIndex    int    `json:"mlineindex"`
	Candidate     string `json:"candidate"`
	SdpMid        string `json:"sdpMid"`
	SdpMLineIndex int    `json:"sdpMLineIndex"`
	Seq           int    `json:"seq"`
	Type          string `json:"type"`
}

func (cand *Candidate) UnmarshalJSON(buf []byte) error {
	var candStr string
	var candObj struct {
		Mid           string `json:"mid"`
		MlineIndex    int    `json:"mlineindex"`
		Candidate     string `json:"candidate"`
		SdpMid        string `json:"sdpMid"`
		SdpMLineIndex int    `json:"sdpMLineIndex"`
		Seq           int    `json:"seq"`
		Type          string `json:"type"`
	}

	if err := json.Unmarshal(buf, &candStr); err == nil {
		if err := json.Unmarshal([]byte(candStr), &candObj); err != nil {
			log.Debugf("could not decode candidate: %s", err)
			return err
		}
		cand.Mid = candObj.Mid
		cand.MlineIndex = candObj.MlineIndex
		cand.Candidate = candObj.Candidate
		cand.SdpMid = candObj.SdpMid
		cand.SdpMLineIndex = candObj.SdpMLineIndex
		cand.Seq = candObj.Seq
		cand.Type = "candidate"
		return nil
	}

	if err := json.Unmarshal(buf, &candObj); err != nil {
		log.Debugf("could not decode candidate: %s", err)
		return err
	}

	cand.Mid = candObj.Mid
	cand.MlineIndex = candObj.MlineIndex
	cand.Candidate = candObj.Candidate
	cand.SdpMid = candObj.SdpMid
	cand.SdpMLineIndex = candObj.SdpMLineIndex
	cand.Seq = candObj.Seq
	cand.Type = "candidate"

	return nil
}

type SessionDescription struct {
	Type        string `json:"type"`
	Description string `json:"sdp"`
}

func (sdp *SessionDescription) UnmarshalJSON(buf []byte) error {
	var sdpStr string
	var sdpObj struct {
		Type        string `json:"type"`
		Description string `json:"sdp"`
	}

	if err := json.Unmarshal(buf, &sdpStr); err == nil {
		if err := json.Unmarshal([]byte(sdpStr), &sdpObj); err != nil {
			log.Debugf("could not decode sdp: %s", err)
			return err
		}
		sdp.Type = sdpObj.Type
		sdp.Description = sdpObj.Description
		return nil
	}

	if err := json.Unmarshal(buf, &sdpObj); err != nil {
		log.Debugf("could not decode sdp: %s", err)
		return err
	}

	sdp.Type = sdpObj.Type
	sdp.Description = sdpObj.Description

	return nil
}

func ParseRequest(buf []byte) (*Request, error) {
	req := Request{}

	err := json.Unmarshal(buf, &req)

	if err != nil {
		log.Warningf("could not parse request: %s", err)
		return nil, proxy.ErrProtocol
	}

	return &req, nil
}

func ParseResponse(buf []byte) (*Response, error) {
	rsp := Response{}

	err := json.Unmarshal(buf, &rsp)

	if err != nil {
		log.Warningf("could not decode api response: %s", err)
		return nil, proxy.ErrProtocol
	}

	return &rsp, nil
}

const (
	OpResponse          = "rsp"
	OpCall              = "initC"
	OpAnswer            = "ansC"
	OpTerminate         = "termC"
	OpCandidate         = "tcklC"
	OpSubscribe         = "subC"
	OpUnSubscribe       = "usubC"
	OpSubscribeRemote   = "subR"
	OpUnSubscribeRemote = "usubR"
	OpPublishRemote     = "pubR"
	OpUnPublishRemote   = "upubR"
	OpJoinMixer         = "joinM"
)

type MetaStatus struct {
	BackendMap map[int]*BackendStatus
	ConfMap    map[string]*ConfStatus
}

type BackendStatus struct {
	TTL       int
	URL       string
	RtcMap    map[int]*RtcStatus
	Connected bool
}

type ConfStatus struct {
	RtcMap map[int]*RtcStatus
}

type RtcStatus struct {
	TTL int
}
